//定义一个订阅者
class Watcher {
    constructor(vm, expr, cb) {
        // 缓冲当前值
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        // 对老值进行存储
        this.oldValue = this.getValue()
    }
    getValue() {
        // 在获取老值的时候，首先将自己添加到全局
        Dep.target = this; // watcher实例
        // 获取已经被劫持的值，会调用 object.defineProperty 的 get 方法，从而将 watcher 添加到订阅器上
        let newValue = CompierUtil.getValue(this.vm,this.expr)
        // 清楚实例，以免重复添加
        Dep.target = null;
        return newValue
    }
    update() {
        // 获取新值
        let newValue = CompierUtil.getValue(this.vm,this.expr)
        if (newValue != this.oldValue) {
            // 调用新值的回掉函数
            this.cb(newValue)
        }
    }
}
class Dep{
    constructor() {
        // 存储订阅者
        this.subs = []
    }
    // 订阅
    addSub(watcher) {
        this.subs.push(watcher)
    }
    // 发布
    notify() {
        // 数据变化时通知订阅者更新
        this.subs.forEach(watcher => {
            watcher.update()
        })
    }
}
class Observer {
    constructor(data) {
        this.observer(data)
    }
    observer(data) {
        // 判断属性值是否为object，只要对象才能做数据劫持
        if (data && typeof data == 'object') {
            for (let key in data) {
                this.defineReactive(data, key, data[key])
            }
        }
    }
    // 对当前属性重新定义
    defineReactive(obj, key, value) {
        // 属性的值如果是对象的话，进行递归定义，以达到所有属性都被监测
        this.observer(value)
        // 实例化一个订阅器到当前属性作用域内，此dep只能被当前属性调用
        let dep = new Dep();
        Object.defineProperty(obj, key, {
            get() {
                // 判断
                console.log(Dep.target)
                // 将订阅者存储（为了不重复存储，当target存在时才执行，执行一次后在 watcher 中设为 null）
                Dep.target && dep.addSub(Dep.target)
                return value
            },
            set: (newValue) => {
                // 当新值发生变化时执行
                if (newValue != value) {
                    // 对新值做监测
                    this.observer(newValue)
                    // 将新值覆盖老值
                    value = newValue
                    // 通知此属性的订阅者进行数据更新
                    dep.notify();
                }
            }
        })
    }
}
